package com.hqbzl.c1.aqs;

import lombok.extern.slf4j.Slf4j;

import java.lang.annotation.Target;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.StampedLock;

/**
 * 不支持条件变量
 * 不支持可重入
 */
@Slf4j
public class TestStampedLock {
    public static void sleep(long t) {
        try {
            TimeUnit.SECONDS.sleep(t);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }


    public static void main(String[] args) {
        DataInfo data = new DataInfo(123);
        new Thread(() -> {
            data.write(456);
        }, "write-thread-1").start();
        // 先加写锁，没释放前，读锁乐观验证会失败，之后升级成读锁
        new Thread(() -> {
            log.debug("data.read() = {}", data.read());
        }, "read-thread").start();
        new Thread(() -> {
            data.write(789);
        }, "write-thread-2").start();
    }

    static class DataInfo {
        final StampedLock lock = new StampedLock();
        private Object data;

        public DataInfo(Object data) {
            this.data = data;
        }

        public Object read() {
            // 先乐观读, 如果验证通过，直接返回
            final long stamp = lock.tryOptimisticRead();
            log.debug("read first stamp: {}", stamp);
            // 在这期间没有被修改
            if (lock.validate(stamp)) {
                return data;
            }
            log.debug("read first stamp validate fail update lock");
            // 锁升升
            final long readLock = lock.readLock();
            log.debug("read lock update start: {}", readLock);
            try {
                sleep(1);
                return data;
            } finally {
                log.debug("read lock update end: {}", readLock);
                lock.unlockRead(readLock);
            }
        }

        public void write(Object object) {
            final long writeLock = lock.writeLock();
            log.debug("write lock start: {}", writeLock);
            try {
                sleep(3);
                this.data = object;
            } finally {
                log.debug("write lock end: {}", writeLock);
                lock.unlockWrite(writeLock);
            }
        }

    }
}
